package com.bpmn.flow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.bpmn.common.enums.TaskStatusEnum;
import com.bpmn.common.pojo.PageResult;
import com.bpmn.common.utils.CollectionUtils;
import com.bpmn.common.utils.MapUtils;
import com.bpmn.flow.controller.query.ProcessTaskQuery;
import com.bpmn.flow.controller.vo.TaskVo;
import com.bpmn.flow.service.TaskQueryService;
import com.bpmn.utils.ModelUtils;
import com.bpmn.utils.QueryUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * @author wangzongrun
 * @description 任务查询相关接口实现类
 * @date 2025年01月09日 15:33
 */
@Slf4j
@Service
public class TaskQueryServiceImpl implements TaskQueryService {

    @Resource
    private TaskService taskService;

    @Override
    public PageResult<TaskVo> queryTaskTodoPage(ProcessTaskQuery pageReqVO) {
        TaskQuery taskQuery = QueryUtils.taskQuery().active().includeTaskLocalVariables().orderByTaskCreateTime().desc();
        if (StrUtil.isNotBlank(pageReqVO.getName())) {
            taskQuery.taskNameLike("%" + pageReqVO.getName() + "%");
        }
        if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
            taskQuery.taskCategory(pageReqVO.getCategory());
        }
        if (StrUtil.isNotEmpty(pageReqVO.getUserId())) {
            taskQuery.taskAssignee(pageReqVO.getUserId());
        }
        List<Task> tasks = taskQuery.listPage(pageReqVO.getFirstNum(), pageReqVO.getPageSize());
        List<ProcessInstance> processInstanceList = QueryUtils.instanceQuery(CollectionUtils.convertSet(tasks, Task::getProcessInstanceId)).list();
        Map<String, ProcessInstance> processInstanceMap = CollStreamUtil.toIdentityMap(processInstanceList, ProcessInstance::getProcessInstanceId);
        List<TaskVo> taskVos = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(tasks)) {
            for (Task task : tasks) {
                TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
                taskVo.setButtonConfigs(ModelUtils.getTaskButtonConfig(task.getProcessDefinitionId(), task.getTaskDefinitionKey()));
                taskVo.setStatus(TaskStatusEnum.RUNNING.getStatus());
                MapUtils.findAndThen(processInstanceMap, task.getProcessInstanceId(), processInstance -> taskVo.setProcessInstanceName(processInstance.getName()));
                taskVos.add(taskVo);
            }
        }
        return new PageResult<>(taskVos, taskQuery.count());
    }

    @Override
    public PageResult<TaskVo> queryTaskDonePage(ProcessTaskQuery pageReqVO) {
        HistoricTaskInstanceQuery taskQuery = QueryUtils.hisTaskInstanceQuery()
                .finished().includeTaskLocalVariables().orderByHistoricTaskInstanceEndTime().desc();
        if (StrUtil.isNotBlank(pageReqVO.getName())) {
            taskQuery.taskNameLike("%" + pageReqVO.getName() + "%");
        }
        if (StrUtil.isNotEmpty(pageReqVO.getCategory())) {
            taskQuery.taskCategory(pageReqVO.getCategory());
        }
        if (StrUtil.isNotEmpty(pageReqVO.getUserId())) {
            taskQuery.taskAssignee(pageReqVO.getUserId());
        }
        List<HistoricTaskInstance> tasks = taskQuery.listPage(pageReqVO.getFirstNum(), pageReqVO.getPageSize());
        List<HistoricProcessInstance> processInstanceList = QueryUtils.hisInstanceQuery(CollectionUtils.convertSet(tasks, HistoricTaskInstance::getProcessInstanceId)).list();
        Map<String, HistoricProcessInstance> processInstanceMap = CollStreamUtil.toIdentityMap(processInstanceList, HistoricProcessInstance::getId);
        List<TaskVo> taskVos = CollUtil.newArrayList();
        if (CollUtil.isNotEmpty(tasks)) {
            for (HistoricTaskInstance task : tasks) {
                TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
                taskVo.setStatus(TaskStatusEnum.APPROVE.getStatus());
                List<Comment> taskComments = taskService.getTaskComments(task.getId());
                //获取最新的一条审批意见
                if (!taskComments.isEmpty()) {
                    taskComments.sort((c1, c2) -> c2.getTime().compareTo(c1.getTime()));
                    Comment latestComment = taskComments.get(0);
                    taskVo.setReason(latestComment.getFullMessage());
                }
                MapUtils.findAndThen(processInstanceMap, task.getProcessInstanceId(), processInstance -> taskVo.setProcessInstanceName(processInstance.getName()));
                taskVos.add(taskVo);
            }
        }
        return new PageResult<>(taskVos, taskQuery.count());
    }

    @Override
    public List<TaskVo> getTaskListByProcessInstanceId(String processInstanceId) {
        List<HistoricTaskInstance> taskInstanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId)
                .includeTaskLocalVariables().includeProcessVariables().orderByHistoricTaskInstanceStartTime().asc().list();
        Map<String, Comment> commentMap = getCommentMapByProcessInstanceId(processInstanceId);
        return historicTaskToTaskVo(taskInstanceList, commentMap);
    }

    @Override
    public Map<String, Comment> getCommentMapByProcessInstanceId(String processInstanceId) {
        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
        return CollectionUtils.convertMap(processInstanceComments, Comment::getTaskId, comment -> comment,
                (existing, replacement) -> existing.getTime().compareTo(replacement.getTime()) > 0 ? existing : replacement);
    }

    public static List<TaskVo> historicTaskToTaskVo(List<HistoricTaskInstance> taskInstanceList, Map<String, Comment> commentMap) {
        List<TaskVo> taskVos = CollUtil.newArrayList();
        for (HistoricTaskInstance task : taskInstanceList) {
            TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
            if (ObjectUtil.isNull(task.getEndTime())) {
                taskVo.setStatus(TaskStatusEnum.RUNNING.getStatus());
            } else {
                taskVo.setStatus(TaskStatusEnum.APPROVE.getStatus());
            }
            MapUtils.findAndThen(commentMap, task.getId(), comment -> {
                taskVo.setOperationType(comment.getType());
                taskVo.setReason(comment.getFullMessage());
            });
            taskVo.setTaskLocalVariables(task.getTaskLocalVariables());
            taskVo.setProcessVariables(task.getProcessVariables());
            taskVos.add(taskVo);
        }
        return taskVos;
    }

}
